/* Copyright 2014 Jan Wolter */
#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "waste.h"
#include "stock.h"
#include "solve.h"
#include "game.h"

char *gamename= "Forty and Eight";
char *gameopt= "S";
Stack *gathered= NULL;
Stack *temp= NULL;

int reservei;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int baserank;

int supermoves= 1;

void setopt(char c)
{
    switch (c)
    {
	case 'S': supermoves= 0; break; /* Disable supermoves */
    }
}

/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    /* Could set things like ndeck, nsuit here */
    ndeck= 2;

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* Create the foundations */
    superfound= sf_acebase;
    for (int s= 0; s < nsuit; s++)
	makeFound(1, s, +1, 13);

    /* Create the tableau */
    for (int i= 1; i <= 8; i++)
	makeTableau(5, FLIP_NONE, shuf,
	    S_SAMESUIT, -1, supermoves ? S_SAMESUIT : S_NONE, -1,
	    1, F_ANYTHING, BREAK_ANYWHERE, 0);

    /* Create the waste */
    makeWaste();

    /* Create the stock */
    makeStock(DT_WASTE, 1, 2, shuf);

    freeStack(shuf);

    /* Print initial state */
    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    int win= solve(gather,0);

    if (verbose > 0) printf("maxoff=%d\n",maxoff);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanTableau();
    cleanWaste();
    cleanStock();
    cleanStacks();
    cleanVisited();
}

/* Gather up the cards into the global 'gathered' stack. This has to be
 * non-destructive, since we may not be done solving when it is called.
 */
void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherFound(gathered);
    gatherTableau(gathered);
    gatherWaste(gathered,0);
    /* We don't gather the stock, because there are never cards in it
     * after a game is over */
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* How good is a move?
 *
 * Returns 0 if the move is completely safe or manditory. Such moves will not
 * be marked as choice points for backtracking.
 *
 * For other moves, positive values are returned with lower values for better
 * moves.
 *
 * Negative values mean the move should not be done at all.
 *
 * So for Forty and Eight:
 *
 *  -1 = Stack moves when there are not enough empty columns to make it
 *       a legal supermove (only when "supermoves" are enabled)
 *     = Moves of a card or stack of cards that were already on their
 *       successor if we have enough empty spaces to do a supermove of more
 *       cards (only when "supermoves" are enabled).
 *
 *   0 = Moves to foundation
 *       Deal a card if waste is empty.
 *
 *   1 = tableau to tableau move that clears an empty space
 *
 *   2,3,4 = tableau to tableau move of a card or stack that was on a card
 *       it didn't fit with. Score is one plus the number of cards that remain
 *       behind in the stack, because we want to encourage drilling to empty
 *       spaces.
 *
 *   90 = waste to tableau move.  Probably need to get smarter about these.
 *
 *   100 = deal from stock
 *
 *   110 = tableau to tableau move of a card or stack that is already on its
 *         successor.
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    if (src->type == STOCK)
    {
	/* A deal. Safe if waste is empty.  Might be possible to also call it
	 * safe if the waste only contains kings, but I'm not totally sure
	 * that would work right, and it's not a common enough situation to
	 * matter. */
	Stack *ws= stacks + waste[0].id;
	return (ws->n == 0 ? 0 : 100);
    }

    if (dst->type == FOUND)
    {
	return 0;
    }

    Card *mc= src->c[index1];

    /* Tableau to tableau moves */
    if (src->type == TABLEAU)
    {
	int willempty= (index1 == 0); /* will the move empty a pile? */
	Card *uc= willempty ? NULL : src->c[index1 - 1]; /* uncovered card */
	int seated= (!willempty && mc->s == uc->s && mc->n == uc->n - 1);

	/* Any stack move is an attempted supermove. Check if there are enough
	 * empty slots.
	 */
	if (len > 1 || (seated && supermoves))
	{
	    /* Count up empty tableau piles, not including the dest pile */
	    int nempty= 0;
	    for (int i= 0; i < ntableau; i++)
	    {
		Stack *s= stacks + tableau[i].id;
		if (s != dst && s->n == 0) nempty++;
	    }
	    /* With n empty slots, we can supermove 2^n cards */
	    int canmove= (1 << nempty);
	    if (len > canmove) return -1;
	    /* If we are moving a seated card, and we have supermoves, then
	     * we should be moving as many cards as we can.
	     */
	    if (seated && supermoves && len < canmove) return -1;
	}

	/* Moves that break up already sorted stacks are low priority */
	if (seated) return 110;

	/* Moves that empty tableau piles are best, but ones that leave
	 * relatively few cards behind are also good */
	return 1 + index1;
    }

    /* Waste to Tableau moves
     * These are complex, but for now we are just going to be simple about it.
     */
    return 90;
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printTableau(f, stks);
    printStock(f, stks);
    printWaste(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Comparision function for stack order normalization in currstate() */
static int cmpstacks(const void *p1, const void *p2)
{
    const Stack *s1= *(const Stack **)p1;
    const Stack *s2= *(const Stack **)p2;
    if (s1->n == 0 || s2->n == 0)
	return s2->n - s1->n;
    int c1= s1->c[0] - deck;
    int c2= s2->c[0] - deck;
    return c2 - c1;
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* ncards for all the cards on things, 8 for foundations,
     * 8 for list terminators.
     */
    char buf[ncard+40], *p= buf;

    /* Numbers of cards in each foundation pile */
    /* We add one to each count because we don't want any zeros */
    for (int k= 0; k < nfound; k++)
	*(p++)= stacks[foundation[k].id].n + 1;

    /* List of cards on each tableau pile - probably we should normalize
     * the order of tableau piles, sorting them by the first card on them.
     * Not sure it's worth the effort for Forty and Eight
     */
#ifdef SORT
    Stack *norm[12];
    for (int k= 0; k < ntableau; k++)
	norm[k]= stacks + tableau[k].id;
    qsort(norm, ntableau, sizeof(Stack *), cmpstacks);

    for (int k= 0; k < ntableau; k++)
    {
	Stack *s= norm[k];
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#else
    for (int k= 0; k < ntableau; k++)
    {
	Stack *s= stacks + tableau[k].id;
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#endif

    /* Number of cards in stock */
    *(p++)= stacks[stock[0].id].n;

    *p= '\0';

    return strdup(buf);
}
